﻿using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Cms.Application.Admin.Views;
using Cms.Application.IO;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;
using Cms.Utilities;

namespace Cms.Application.Admin.Presenters
{
    public class EditFolderPresenter : BasePresenter
    {
        private readonly Administrator _administrator;
        private readonly IEditFolderView _view;

        // Public property for testability
        public IDiskAccess DiskAccess { get; set; }

        public EditFolderPresenter(IEditFolderView view)
            : this(view, new RepositoryFactory())
        {
            DiskAccess = new DiskAccess();
        }
        
        public EditFolderPresenter(IEditFolderView view, IRepositoryFactory repositoryFactory)
            : base(view, repositoryFactory)
        {
            _view = view;
            _administrator = AdministratorRepository.GetAdministrator(_view.AdministratorID);
            _view.HeadingText = GetText(_view, _view.FolderID > 0 ? "EditFolderHeading" : "NewFolderHeading");
            if (!_administrator.HasPermission.EditFolder)
                _view.RedirectToPermissionDeniedPage();
        }
        
        public void Initialize()
        {
            _view.SetParentFolders(_administrator.CurrentSite.Domain, _administrator.CurrentSite.GetRootFolders());
            if (_view.FolderID == -1)
            {
                _view.SelectedParentFolderID = FolderRepository.GetFolder(_administrator.SelectedFolderID) != null
                                                   ? _administrator.SelectedFolderID
                                                   : -1;
            }
            else
            {
                Folder folder = FolderRepository.GetFolder(_view.FolderID);
                _view.Name = folder.Name;
                if (folder.Parent == null)
                    _view.SelectedParentFolderID = -1;
                else
                    _view.SelectedParentFolderID = folder.Parent.ID;
            }
            Folder parentFolder = FolderRepository.GetFolder(_view.SelectedParentFolderID);
            Folder selectedFolder = FolderRepository.GetFolder(_view.FolderID);

            // Set up categories
            IList<Category> parentFolderCategories = parentFolder != null
                                                         ? parentFolder.GetAllCategories()
                                                         : new List<Category>();
            IList<Category> selectedFolderCategories = selectedFolder != null
                                                           ? selectedFolder.Categories
                                                           : new List<Category>();
            _view.SetCategories(_administrator.CurrentSite.GetRootCategories(), parentFolderCategories, selectedFolderCategories);

            // Set up allowed administrator groups
            IList<AdministratorGroup> parentFolderAllowedAdministratorGroups = parentFolder != null
                                                                                   ? parentFolder.GetAllAllowedAdministratorGroups()
                                                                                   : new List<AdministratorGroup>();
            IList<AdministratorGroup> selectedFolderAllowedAdministratorGroups = selectedFolder != null
                                                                                     ? selectedFolder.AllowedAdministratorGroups
                                                                                     : new List<AdministratorGroup>();
            _view.SetAllowedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                parentFolderAllowedAdministratorGroups,
                selectedFolderAllowedAdministratorGroups);

            // Set up denied administrator groups
            IList<AdministratorGroup> parentFolderDeniedAdministratorGroups = parentFolder != null
                                                                                  ? parentFolder.GetAllDeniedAdministratorGroups()
                                                                                  : new List<AdministratorGroup>();
            IList<AdministratorGroup> selectedFolderDeniedAdministratorGroups = selectedFolder != null
                                                                                    ? selectedFolder.DeniedAdministratorGroups
                                                                                    : new List<AdministratorGroup>();
            _view.SetDeniedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                parentFolderDeniedAdministratorGroups,
                selectedFolderDeniedAdministratorGroups);

            // Set up required user groups
            IList<UserGroup> parentFolderRequiredUserGroups = parentFolder != null
                                                                  ? parentFolder.GetRequiredUserGroups()
                                                                  : new List<UserGroup>();
            IList<UserGroup> selectedFolderRequiredUserGroups = selectedFolder != null
                                                                    ? selectedFolder.RequiredUserGroups
                                                                    : new List<UserGroup>();
            IList<UserGroup> selectedFolderDeselectedUserGroups = selectedFolder != null
                                                                      ? selectedFolder.DeselectedUserGroups
                                                                      : new List<UserGroup>();
            _view.SetRequiredUserGroups(
                UserRepository.GetUserGroups(), 
                parentFolderRequiredUserGroups,
                selectedFolderRequiredUserGroups, 
                selectedFolderDeselectedUserGroups);
        }

        public void UpdateCategoriesAndGroups()
        {
            Folder parentFolder = FolderRepository.GetFolder(_view.SelectedParentFolderID);
            IList<Category> parentFolderCategories = parentFolder != null
                                                         ? parentFolder.GetAllCategories()
                                                         : new List<Category>();
            _view.SetCategories(_administrator.CurrentSite.GetRootCategories(), parentFolderCategories, _view.GetSelectedCategories().ToList());

            IList<AdministratorGroup> parentFolderAllowedAdministratorGroups = parentFolder != null
                                                                                   ? parentFolder.GetAllAllowedAdministratorGroups()
                                                                                   : new List<AdministratorGroup>();
            _view.SetAllowedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                parentFolderAllowedAdministratorGroups,
                _view.GetSelectedAllowedAdministratorGroups().ToList());
            IList<AdministratorGroup> parentFolderDeniedAdministratorGroups = parentFolder != null
                                                                                  ? parentFolder.GetAllDeniedAdministratorGroups()
                                                                                  : new List<AdministratorGroup>();
            _view.SetDeniedAdministratorGroups(
                AdministratorRepository.GetCustomGroups(),
                parentFolderDeniedAdministratorGroups,
                _view.GetSelectedDeniedAdministratorGroups().ToList());
            IList<UserGroup> parentFolderRequiredUserGroups = parentFolder != null
                                                                  ? parentFolder.GetRequiredUserGroups()
                                                                  : new List<UserGroup>();
            _view.SetRequiredUserGroups(
                UserRepository.GetUserGroups(),
                parentFolderRequiredUserGroups,
                _view.GetSelectedRequiredUserGroups().ToList(),
                _view.GetDeselectedUserGroups().ToList());
        }
        
        public void Save()
        {
            Validate();
            if (!IsValid)
                return;
            Folder folder;
            string originalVirtualPath = string.Empty;
            if (_view.FolderID == -1)
            {
                folder = new Folder { Site = _administrator.CurrentSite };
            }
            else
            {
                folder = FolderRepository.GetFolder(_view.FolderID);
                originalVirtualPath = folder.GetVirtualPath();
            }

            // Collect paths before changing the folders name and/or location to be able to update all references
            IDictionary<int, string> originalPaths = new Dictionary<int, string>();
            CollectOriginalPathsForUpdate(folder, ref originalPaths);

            // Once the paths are collected, get the new settings
            folder.Name = _view.Name;
            folder.Parent = FolderRepository.GetFolder(_view.SelectedParentFolderID);

            // Clear and collect categories
            foreach (Category category in folder.Categories)
                category.Folders.Remove(folder);
            folder.Categories.Clear();
            foreach (Category category in _view.GetSelectedCategories())
            {
                folder.Categories.Add(category);
                category.Folders.Add(folder);
            }

            // Clear and collect allowed administrator groups
            foreach (AdministratorGroup group in folder.AllowedAdministratorGroups)
                group.AllowedFolders.Remove(folder);
            folder.AllowedAdministratorGroups.Clear();
            foreach (AdministratorGroup group in _view.GetSelectedAllowedAdministratorGroups())
            {
                folder.AllowedAdministratorGroups.Add(group);
                group.AllowedFolders.Add(folder);
            }

            // Clear and collect denied administrator groups
            foreach (AdministratorGroup group in folder.DeniedAdministratorGroups)
                group.DeniedFolders.Remove(folder);
            folder.DeniedAdministratorGroups.Clear();
            foreach (AdministratorGroup group in _view.GetSelectedDeniedAdministratorGroups())
            {
                folder.DeniedAdministratorGroups.Add(group);
                group.DeniedFolders.Add(folder);
            }

            // Clear and collect required user groups
            IList<UserGroup> removedUserGroups = new List<UserGroup>();
            foreach (UserGroup group in folder.RequiredUserGroups)
            {
                removedUserGroups.Add(group);
            }
            foreach (UserGroup group in folder.RequiredUserGroups)
            {
                group.SelectedFolders.Remove(folder);
            }
            folder.RequiredUserGroups.Clear();
            foreach (UserGroup group in _view.GetSelectedRequiredUserGroups())
            {
                folder.RequiredUserGroups.Add(group);
                group.SelectedFolders.Add(folder);
            }

            // Find out which groups that were removed
            foreach (UserGroup group in folder.RequiredUserGroups)
                if (removedUserGroups.Contains(group))
                    removedUserGroups.Remove(group);
            folder.RemoveDeselectedUserGroups(removedUserGroups);
            foreach (UserGroup group in folder.DeselectedUserGroups)
                group.DeselectedFolders.Remove(folder);
            folder.DeselectedUserGroups.Clear();
            foreach (UserGroup group in _view.GetDeselectedUserGroups())
            {
                folder.DeselectedUserGroups.Add(group);
                group.DeselectedFolders.Add(folder);
            }
            FolderRepository.SaveFolder(folder);

            // Create/move the physical folder
            if (_view.FolderID == -1)
            {
                //DiskAccess.CreateFolder(folder.GetVirtualPath());
            }
            else
            {
                //DiskAccess.MoveFolder(originalVirtualPath, folder.GetVirtualPath());
            }    

            // Update all references
            var frm = new FileReferencesManager(_administrator, SiteRepository, ContentRepository, DiskAccess);
            foreach (var kvp in originalPaths)
            {
                originalVirtualPath = kvp.Value;
                string newVirtualPath = PageRepository.GetPage(kvp.Key).GetVirtualPath();
                frm.UpdateFileReferences(originalVirtualPath, newVirtualPath, _administrator.CurrentSite.ID);
            }
        }
        
        // Collect paths recursively
        public void CollectOriginalPathsForUpdate(Folder folder, ref IDictionary<int, string> paths)
        {
            foreach (Page page in folder.Pages)
                paths.Add(page.ID, page.GetVirtualPath());
            foreach (Folder f in folder.SubFolders)
                CollectOriginalPathsForUpdate(f, ref paths);
        }

        private void Validate()
        {
            if (_view.Name.Trim() == string.Empty)
                AddValidationError(1, GetText(_view, "NoNameError"));
            else
            {
                if (!RegexValidation.NameIsFileSystemCompliant(_view.Name.Trim()))
                {
                    AddValidationError(2, GetText(_view, "NameNotFileSystemCompliantError"));
                }
                bool isReservedName = false;
                if (_view.Name.Trim().ToLower() == "bin" || _view.Name.Trim().ToLower() == "__system" ||
                    _view.Name.Trim().ToLower() == "admin" || _view.Name.Trim().ToLower() == "swfupload")
                    isReservedName = true;
                foreach (
                    Site site in
                        SiteRepository.GetSites().Where(
                            site => _view.Name.Trim().ToLower() == site.RootMediaFolder.ToLower()))
                    isReservedName = true;
                foreach (
                    string folderName in
                        ConfigurationManager.AppSettings["ReservedFolderNames"].Split(",".ToCharArray()).Where(
                            folderName => _view.Name.Trim().ToLower() == folderName.Trim().ToLower()))
                    isReservedName = true;
                if (isReservedName)
                    AddValidationError(2, GetText(_view, "ReservedNameError"));
            }
            if (_view.FolderID > 0 && _view.SelectedParentFolderID == _view.FolderID)
                AddValidationError(3, GetText(_view, "SaveInSelfError"));
            if (_view.FolderID > 0)
            {
                Folder editedFolder = FolderRepository.GetFolder(_view.FolderID);
                Folder selectedParentFolder = FolderRepository.GetFolder(_view.SelectedParentFolderID);
                if (editedFolder.IsParentTo(selectedParentFolder))
                    AddValidationError(4, GetText(_view, "SaveInChildError"));
            }
            if (!_administrator.CurrentSite.CanFolderBeNamedAndMovedTo(_view.FolderID, _view.Name, FolderRepository.GetFolder(_view.SelectedParentFolderID)))
            {
                AddValidationError(5, GetText(_view, "NameCollisionError"));
            }
        }
    }
}